package net.protocol;

import net.buffer.ByteBuffer;
import net.NetClient;
import net.NetConfig;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;

public class NetMsgCenter {

    private final static NetMsgCenter msgCenter = new NetMsgCenter();
    public static NetMsgCenter getInstance()
    {
        return msgCenter;
    }
    private Map<Integer, Class<?>> types = new HashMap<>();
    private Map<Integer, List<Consumer<NetMsgArg>>> handlers = new HashMap<>();

    private NetMsgCenter()
    {
    }

    //服务端不主动发送消息
    public <T extends IProtocol> void sendAsync(NetMsgArg arg)
    {
        arg.client().writeAsync(serialize(arg.content()));
    }

    public void register(NetClient client)
    {
        client.receiveHandler(this::receive);
    }

    public void unregister(NetClient client)
    {
        client.receiveHandler(null);
    }

    private void receive(NetClient client, ByteBuffer buffer)
    {
        buffer.read();
        int pt = buffer.getInt();
        if(handlers.containsKey(pt))
        {
            handlers.get(pt).forEach(handler->{
                NetMsgArg arg = new NetMsgArg().client(client);
                arg.content(deserialize(types.get(pt),buffer));
                handler.accept(arg);
            });
        }
    }

    public synchronized <T extends IProtocol> void addMsgListener(Class<T> type, Consumer<NetMsgArg> handler)
    {
        try {
            Object instance = type.getDeclaredConstructor().newInstance();
            Method protocolType = type.getDeclaredMethod("protocolType");
            // 设置访问权限，可以访问私有的方法
            // nonStaticMethod.setAccessible(true);
            int pt = (int)protocolType.invoke(instance);
            if(!types.containsKey(pt))
            {
                types.put(pt, type);
            }
            if(!handlers.containsKey(pt)) handlers.put(pt, new ArrayList<>());
            handlers.get(pt).add(handler);
        } catch (NoSuchMethodException e) {
            throw new RuntimeException("NoSuchMethodException");
        } catch (InvocationTargetException | InstantiationException | IllegalAccessException e) {
            throw new RuntimeException(e);
        }
//        if(type.isAnnotationPresent(Index.class)){
//            int pt = type.getAnnotation(Index.class).value();
//            if(!types.containsKey(pt))
//            {
//                types.put(pt, type);
//            }
//            if(!handlers.containsKey(pt)) handlers.put(pt, new ArrayList<>());
//            handlers.get(pt).add(handler);
//        }
    }

    @SuppressWarnings("unchecked")
    public <T extends IProtocol> T deserialize(Class<?> type, ByteBuffer buf)
    {
        try {
            Object instance = type.getDeclaredConstructor().newInstance();
            T t = (T)  instance;
            t.deserialize(buf);
            return t;
        } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
            throw new RuntimeException(e);
        }
    }

    private <T extends IProtocol> ByteBuffer serialize(T t)
    {
        ByteBuffer ts = t.serialize();
        int len = ts.remaining();
        ts.read();
//        System.out.println("object "+ ts);
        ByteBuffer buffer = new ByteBuffer(len + NetConfig.LENGTH_TYPE);
        buffer.putInt(t.protocolType());
        buffer.fill(ts);
        buffer.read();
//        System.out.println("type object "+ buffer);
        return buffer;
    }
}
